package com.chrilwe.pigpig.auth.service;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.TimeUnit;

import javax.imageio.ImageIO;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import com.chrilwe.pigpig.auth.common.response.ResultEnum;
import com.chrilwe.pigpig.auth.common.response.ValidateCodeAndCat;
import com.chrilwe.pigpig.auth.common.response.ValidateCodeResult;
import com.chrilwe.pigpig.auth.mapper.ValidateCodeMapper;

import cn.chrilwe.pigpig.core.model.auth.ValidateCode;
import cn.chrilwe.pigpig.core.model.auth.ValidateCodeCategory;

/**
 * 验证码生成
 * 
 * @author chrilwe 2020-8-24
 */
@Service
public class ValidateCodeService {

	@Autowired
	private StringRedisTemplate stringRedisTemplate;
	
	@Autowired
	private ValidateCodeMapper validateCodeMapper;

	private static final int width = 100;

	private static final int height = 40;

	private static Color backColor;

	private static Color lineColor;

	private static Color foreColor;

	private boolean randomLocation = true;

	private static int interLine = 5;
	
	private static final int codeTimeout = 5;

	public BufferedImage getCodeImage(String clientId) {
		
		return draw(clientId);
	}
	
	public ValidateCodeResult getCodeImageUrls(String clientId) {
		ValidateCodeAndCat validateCode = getValidateCode();
		if(validateCode != null) {
			ValidateCodeCategory codeCat = validateCode.getCodeCat();
			List<ValidateCode> codes = validateCode.getCodes();
			Map<Integer, String> map = new HashMap<Integer, String>();
			String ids = "";
			for (ValidateCode c : codes) {
				String imageUrl = c.getImageUrl();
				map.put(c.getId(), c.getImageUrl());
				ids += c.getId() + ",";
			}
			
			String question = "请找出图中"+codeCat.getName()+"的图片";
			
			ids.substring(0, ids.lastIndexOf(","));
			saveTmpCode("Tmp"+clientId, ids);
			
			ValidateCodeResult res = new ValidateCodeResult(ResultEnum.SUCCESS.getCode(), ResultEnum.SUCCESS.getName(), map, question);
		}
		return new ValidateCodeResult(ResultEnum.UNKNOW_ERR.getCode(), ResultEnum.UNKNOW_ERR.getName(), null, "");
	}
	
	protected ValidateCodeAndCat getValidateCode() {
		//随机在数据库中获取一条验证码分类
		ValidateCodeCategory codeCat = validateCodeMapper.selectValidateCodeCatByRand(1);
		if(codeCat != null) {
			int catId = codeCat.getCatId();
			
			List<ValidateCode> codes = validateCodeMapper.selectValidateCodeByCatIdAndRand(1, catId);
			ValidateCodeAndCat res = new ValidateCodeAndCat();
			res.setCodeCat(codeCat);
			res.setCodes(codes);
			return res;
		}
		return null;
	}
	
	public boolean checkCode(String clientId, String code) {
		if(StringUtils.isAnyEmpty(clientId,code)) {
			return false;
		}
		String realCode = stringRedisTemplate.opsForValue().get(clientId);
		if(StringUtils.isEmpty(realCode) || !realCode.equals(code)) {
			return false;
		}
		return true;
	}

	protected BufferedImage draw(String clientId) {
		BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
		Graphics graphics = image.getGraphics();

		Random r = new Random();

		graphics.setColor(backColor == null ? getRandomColor(r) : backColor);
		graphics.fillRect(0, 0, width, height);

		// 生成字符
		String textCode = generateCode(clientId);

		// 绘制干扰线
		drawLine(graphics, r);

		// 绘制字符
		drawString(graphics, textCode, r);

		// 扭曲图片
		shearX(r, graphics, width, height, getRandomColor(r));
		shearY(r, graphics, width, height, getRandomColor(r));
		
		//绘制噪点
		drawFloat(r, image);
		graphics.dispose();
		return image;
	}

	protected Color getRandomColor(Random random) {
		Color color = new Color(random.nextInt(5)+250, random.nextInt(5)+240, random.nextInt(8)+230);
		return color;
	}
	
	//72 61 139    69 139 0  255 193 37
	protected Color getStringRandomColor(Random random) {
		List<Integer[]> list = new ArrayList<Integer[]>();
		Integer[] a = {72,61,139};
		Integer[] b = {69, 139, 0};
		Integer[] c = {255, 193, 37};
		list.add(a);list.add(b);list.add(c);
		int num = random.nextInt(list.size());
		Integer[] colorVal = list.get(num);
		Color color = new Color(colorVal[0], colorVal[1], colorVal[2]);
		return color;
	}

	protected void drawLine(Graphics graphics, Random r) {
		if (interLine > 0) {
			int x = r.nextInt(4), y = 0;
			int x1 = width - r.nextInt(4), y1 = 0;
			for (int i = 0; i < interLine; i++) {
				graphics.setColor(lineColor == null ? getRandomColor(r) : lineColor);
				y = r.nextInt(height - r.nextInt(4));
				y1 = r.nextInt(height - r.nextInt(4));
				graphics.drawLine(x, y, x1, y1);
			}
		}
	}

	protected void drawString(Graphics g, String textCode, Random r) {
		int fsize = (int) (height * 0.7);// 字体大小为图片高度的60%
		int fx = 0;
		int fy = fsize;
		g.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, fsize));
		for (int i = 0; i < textCode.length(); i++) {
			//fy = randomLocation ? (int) ((Math.random() * 0.3 + 0.6) * height) : fy;// 每个字符高低是否随机
			g.setColor(foreColor == null ? getStringRandomColor(r) : foreColor);
			g.drawString(textCode.charAt(i) + "", fx, fy);
			fx += (width / textCode.length()) * (Math.random() * 0.3 + 0.7); // 依据宽度浮动
		}
	}

	protected String generateCode(String clientId) {
		String[] array = { "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "a", "b", "c", "d", "e", "f", "g", "h",
				"i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z" };

		int len = array.length;
		Random random = new Random();
		String str = "";
		for (int i = 0; i < 4; i++) {
			int n = random.nextInt(len);
			str += array[n];
		}
		
		saveTmpCode(clientId, str);
		return str;
	}
	
	protected void saveTmpCode(String clientId, String code) {
		stringRedisTemplate.opsForValue().set(clientId, code, codeTimeout, TimeUnit.MINUTES);
	}

	protected void shearX(Random random, Graphics g, int w1, int h1, Color color) {
		int period = 2;
		boolean borderGap = true;
		int frames = 1;
		int phase = random.nextInt(2);

		for (int i = 0; i < h1; i++) {
			double d = (double) (period >> 1)
					* Math.sin((double) i / (double) period + (2.2831853071795862D * (double) phase) / (double) frames);
			g.copyArea(0, i, w1, 1, (int) d, 0);
			if (borderGap) {
				g.setColor(color);
				g.drawLine((int) d, i, 0, i);
				g.drawLine((int) d + w1, i, w1, i);
			}
		}

	}

	protected void shearY(Random random, Graphics g, int w1, int h1, Color color) {
		int period = random.nextInt(40) + 10; // 50;

		boolean borderGap = true;
		int frames = 20;
		int phase = random.nextInt(2);
		for (int i = 0; i < w1; i++) {
			double d = (double) (period >> 1)
					* Math.sin((double) i / (double) period + (2.2831853071795862D * (double) phase) / (double) frames);
			g.copyArea(i, 0, 1, h1, 0, (int) d);
			if (borderGap) {
				g.setColor(color);
				g.drawLine(i, (int) d, i, 0);
				g.drawLine(i, (int) d + h1, i, h1);
			}

		}

	}

	protected void drawFloat(Random r, BufferedImage image) {
		float yawpRate = 0.05f;// 噪声率
		int area = (int) (yawpRate * width * height);// 噪点数量
		for (int i = 0; i < area; i++) {
			int xxx = r.nextInt(width);
			int yyy = r.nextInt(height);
			int rgb = getRandomColor(r).getRGB();
			image.setRGB(xxx, yyy, rgb);
		}
	}
}
